package org.study.itcast.socket;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import static org.study.ByteBufferUtil.debugAll;

/**
 * @author: jzhou
 * @date: 2024/4/21-21:45
 * @version: 1.0
 * @description: spark3.3-study
 * 在 ServerSelectorDemo6 中存在 客户端一断开 服务端就会报错 关闭的异常 在本次demo中 添加了异常处理
 */
@Slf4j
public class ServerSelectorDemo2 {
    public static void main(String[] args) {
        try (ServerSocketChannel ssc = ServerSocketChannel.open()) {
            ssc.bind(new InetSocketAddress(8080));
            log.debug(ssc.toString());
            // 1. 建立selector 管理多个channel
            Selector selector = Selector.open();
            ssc.configureBlocking(false);
            // 2. 将channel 注册到selector，建立二者的联系
            SelectionKey register = ssc.register(selector, SelectionKey.OP_ACCEPT, null);
            log.debug("key {}", register);  // 多个线程 建立多个channel 但 SelectionKey 是同一个
            while (true) {
                // 3. select 方法 没有事件发生 线程阻塞，有事件发生 线程唤醒恢复运行
                // 有未处理事件时是不会阻塞的
                int count = selector.select();

                log.debug("select count: {}", count);

                // 获取所有事件
                Set<SelectionKey> keys = selector.selectedKeys();
                log.debug("key {}", register);  // 多个线程 建立多个channel 但 SelectionKey 是同一个
                log.info("keys size {}" , keys.size());
                // 4. 处理事件 SelectionKey 包含所有事件， 遍历所有事件，逐一处理
                Iterator<SelectionKey> iter = keys.iterator();

                while (iter.hasNext()) {
                    SelectionKey key = iter.next();
                    log.debug("key {}", key);
//                    key.cancel();
                    // 判断事件类型
                    try {
                        if (key.isAcceptable()) {
                            ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                            // 必须处理
                            SocketChannel sc = serverSocketChannel.accept();
                            log.debug("{}", sc); // 建立的 channel 是新的channel
                            sc.configureBlocking(false);
                            sc.register(selector, SelectionKey.OP_READ);
                        }else if (key.isReadable()) {
                            SocketChannel channel = (SocketChannel) key.channel();  // 获取channel
                            log.debug("{}", channel);
                            ByteBuffer byteBuffer = ByteBuffer.allocate(4);
                            int read = channel.read(byteBuffer);
                            if (read == -1) { // 无论正常退出还是异常关闭客户端 都会出发一次读事件， 正常退出时 read = -1
                                key.cancel();
                            }else {
                                byteBuffer.flip();
                                debugAll(byteBuffer);
                            }
                        }
                        // 处理key 或者 处理完毕时，必须将事件移除，否者下次处理会有问题
                        iter.remove();
                    }catch (IOException e) {
                        log.error("客户端异常关闭，手动cancel 将key移除 否则selector.select() 中一直有未处理事件，程序会一直异常", e);
                        key.cancel();
                    }

                }
            }
        } catch (IOException e) {
            log.error("connection error" + e);

        }
    }
}